package j.util.jdbc.simple.core.support;

import j.util.New;
import j.util.StringUtils;
import j.util.jdbc.JdbcUtils;
import j.util.jdbc.simple.annotation.DataType;
import j.util.jdbc.simple.annotation.Table;
import j.util.jdbc.simple.core.SQLData;
import j.util.jdbc.simple.core.SimpleJdbcContext;
import j.util.jdbc.simple.metadata.Column;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * 针对simplejdbc包的一些常用辅助方法
 *
 */
public class SimpleJdbcUtils {

    public static Map<String,Object> fromEntityToMapWithPk(Object entity){
        try{
            Class<?> clazz = entity.getClass();
            Table table = clazz.getAnnotation(Table.class);
            String pkColumns = table.pkColumns();
            Set<String> set = StringUtils.asLSet(pkColumns, ",");
            Map<String,Object> map = New.lMap();
            for(String s : set){
                Field f = clazz.getDeclaredField(s);
                if(f!=null){
                    f.setAccessible(true);
                    map.put(s.toUpperCase(),f.get(entity));
                }
            }
            return map;
        }catch(Exception e){
            throw new RuntimeException(String.format("无法从实体[%s]中获取主键!",entity.getClass().getName()));
        }
    }

	/**
	 * 将实体转换为map，key为所映射的字段名，value为该实体的值
	 * @param entity
	 * @return
	 */
	public static Map<String,Object> fromEntityToMap(Object entity){
		Map<String,Object> map = New.map();
        parseEntityToMap(map,entity,entity.getClass());
		return map;
	}

    private static void parseEntityToMap(Map<String,Object> map,Object entity,Class<?> entityClass){
        String key ;
        Object value;
        for(Field f : entityClass.getDeclaredFields()){
            j.util.jdbc.simple.annotation.Column c = f.getAnnotation(j.util.jdbc.simple.annotation.Column.class);
            if(null==c || StringUtils.isNullOrEmpty(c.name())){
                key = f.getName().toUpperCase();
            }else{
                if(c.noUsed()){
                    //不作为字段处理，直接处理下一个
                    continue;
                }
                key = c.name().toUpperCase();
            }
            try {
                f.setAccessible(true);
                value = f.get(entity);
            } catch (Exception e) {
                value = null;
            }
            map.put(key, value);
        }
        Class<?> superClazz = entityClass.getSuperclass();
        if(null!=superClazz){
            parseEntityToMap(map,entity,superClazz);
        }
    }
	
	/**
	 * 从一个实体类中获取表的名字，先看其是否有DbTable注解，若有，则直接用其配置，负责取类的名字，不含包名
	 * 最终将以大写形式返回
	 * @return
	 */
	public static String getTableNameFromEntity(Class<?> clazz){
		Table table = (Table)clazz.getAnnotation(Table.class);
		String tblName = "";
		if(null != table){
			if(StringUtils.isNotEmpty(table.name())){
				tblName = table.name();
			}
		}
		if(StringUtils.isNullOrEmpty(tblName)){
			//没有指定表名字，则用类的名字，且将包名去掉
			tblName = clazz.getName();
			int li = tblName.lastIndexOf(".");
			if(li>-1){
				tblName = tblName.substring(li+1);
			}
		}
		return tblName.toUpperCase();
	}
	/**
	 * 将map中的key全转换为大写字母
	 * @param map
	 * @return
	 */
	public static Map<String,Object> setKeyToUpper(Map<String,Object> map){
		Map<String,Object> _map = New.map();
		for(Map.Entry<String, Object> entry : map.entrySet()){
			_map.put(entry.getKey().toUpperCase(), entry.getValue());
		}
		return _map;
	}

	public static String computeJdbcParameters(int size){
		StringBuilder sb = new StringBuilder();
		for(int i=0;i<size;i++){
			if(i>0){
				sb.append(",");
			}
			sb.append("?");
		}
		return sb.toString();
	}
	public static Object[] fromMapArgsToArrayArgs(Map<String,Column> metaData,
			List<String> pOrder,Map<String,Object> args){
		Object[] object = new Object[args.size()];
		Object value = null;
		Column column;
		int endIndex = 0;
		for(int i=0;i<pOrder.size();i++){
			value = args.get(pOrder.get(i));
			//检查类型
			column = metaData.get(pOrder.get(i));
			if(null == column && (endIndex=pOrder.get(i).lastIndexOf(SqlUtils.WHERE_KEY_SUFFIX))>-1){
				column = metaData.get(pOrder.get(i).substring(0, endIndex));
			}
			if(column.getDataType().equals(DataType.DATETIME)){
				Date date = (Date)value;
				Timestamp sqlDate = new Timestamp(date.getTime());
				value = sqlDate;
			}else if(column.getDataType().equals(DataType.DATE)){
				Date date = (Date)value;
				java.sql.Date sqlDate = new java.sql.Date(date.getTime());
				value = sqlDate;
			}
			object[i] = value;
		}
		return object;
	}
	/**
	 * 为preparedStatement设置参数
	 * 在这里过滤各种数据类型
	 * @param stmt preparedStatement对象
	 * @param pOrder 参数顺序
	 * @param args 参数集
	 * @throws java.sql.SQLException
	 */
	public static void setPreapredStatementParameters(Map<String,Column> metaData,PreparedStatement stmt,List<String> pOrder,Map<String,Object> args) throws SQLException{
		if(null == pOrder || pOrder.size() == 0)
			return;
		Object value = null;
		Column column;
		int endIndex = 0;
		for(int i=0;i<pOrder.size();i++){
			value = args.get(pOrder.get(i));
			//检查类型
			if(null!=value){
				column = metaData.get(pOrder.get(i));
				if(null == column && (endIndex=pOrder.get(i).lastIndexOf(SqlUtils.WHERE_KEY_SUFFIX))>-1){
					column = metaData.get(pOrder.get(i).substring(0, endIndex));
				}
				if(column.getDataType().equals(DataType.DATETIME)){
					Date date = (Date)value;
					Timestamp sqlDate = new Timestamp(date.getTime());
					value = sqlDate;
				}else if(column.getDataType().equals(DataType.DATE)){
					Date date = (Date)value;
					java.sql.Date sqlDate = new java.sql.Date(date.getTime());
					value = sqlDate;
				}
			}
			stmt.setObject(i+1, value);
		}
	}
	/**
	 * 将数组转换成set集合，仅支持字符串类型
	 * @param array
	 * @return
	 */
	public static Set<String> convertArrayToSet(String[] array){
		Set<String> set = New.set();
		for(int i=0;i<array.length;i++){
			set.add(array[i]);
		}
		return set;
	}
    public static <T> T fromResultSetToPojo_old(Class<T> requiredType,
                                            ResultSet rs,Set<String> dbColumns){
        T t;
        try{
            t = requiredType.newInstance();
            j.util.jdbc.simple.annotation.Column c;
            SQLData sd = SimpleJdbcContext.getSQLData(requiredType);
            Collection<Column> list = sd.getTmd().getColumns().values();
            for(Column column : list){
                Field f = requiredType.getDeclaredField(column.getEntityAttributeName());
                if(null!=f){
                    f.setAccessible(true);
                    f.set(t,rs.getObject(column.getName()));
                }
            }
            /*for(Field f : requiredType.getDeclaredFields()){
                c = f.getAnnotation(j.j.util.jdbc.simple.annotation.Column.class);
                db_field_name = f.getName();
                if(null!=c && StringUtils.isNotEmpty(c.name())){
                    db_field_name = c.name();
                }
                if(dbColumns.contains(db_field_name.toUpperCase())){
                    f.setAccessible(true);
                    BeanUtils.setProperty(t, f.getName(), rs.getObject(db_field_name));
                }
            }*/
            return t;
        }catch(Exception e){
            throw new RuntimeException(String.format("将结果集转换为POJO类[%s]时异常,异常信息:%s.",requiredType.getName(),e.getMessage()));
        }
    }
	/**
	 * 将resultset的一记录转换为一个POJO
	 * dbColumns在从数据集中获取数据前判断是否存在这个字段，若不存在，则不获取
	 * @param requiredType
	 * @param rs
	 * @return
	 * @throws Exception
	 */
	public static <T> T fromResultSetToPojo(Class<T> requiredType, ResultSet rs){
        T t;
        try{
            t = requiredType.newInstance();
            j.util.jdbc.simple.annotation.Column c;
            SQLData sd = SimpleJdbcContext.getSQLData(requiredType);
            Collection<Column> list = sd.getTmd().getColumns().values();
            DataType dataType;
            Class<?> fieldType;
            Field f;
            for(Column column : list){
                f = null;
                try{
                    f = requiredType.getDeclaredField(column.getEntityAttributeName());
                }catch (NoSuchFieldException ignored){}
                if(null!=f){
                    f.setAccessible(true);
                    dataType = column.getDataType();
                    fieldType = f.getType();   //modify start by hht 2014-12-29
                    if(dataType.equals(DataType.AUTO)) {
                        if(fieldType.equals(String.class)){
                            f.set(t, rs.getString(column.getName()));
                        }else if(fieldType.equals(int.class)){
                            f.setInt(t,rs.getInt(column.getName()));
                        }else if(fieldType.equals(Integer.class)){
                            f.set(t,JdbcUtils.getInteger(rs,column.getName()));
                        }else if(fieldType.equals(Date.class)){
                            //日期类型
                            f.set(t,new Date(rs.getTimestamp(column.getName()).getTime()));
                        }else if(fieldType.equals(Boolean.class)){
                            f.set(t,JdbcUtils.getBoolean(rs,column.getName()));
                        }else if(fieldType.equals(boolean.class)){
                            Boolean b = JdbcUtils.getBoolean(rs,column.getName());
                            if(b != null){
                                f.setBoolean(t,b);
                            }else{
                                f.setBoolean(t,false);
                            }
                        }else{
                            f.set(t,rs.getObject(column.getName()));
                        }
                    }else {                       //modify end by hht 2014-12-29
                        if(dataType.equals(DataType.BOOLEAN)){
                            f.set(t,rs.getBoolean(column.getName()));
                        }else if(dataType.equals(DataType.DATE)){
                            f.set(t,rs.getDate(column.getName()));
                        }else if(dataType.equals(DataType.DATETIME)){
                            f.set(t, JdbcUtils.getDateFromResultSet(rs,column.getName()));
                        }else if(dataType.equals(DataType.DOUBLE)){
                            f.set(t,JdbcUtils.getDouble(rs,column.getName()));
                        }else if(dataType.equals(DataType.FLOAT)){
                            f.set(t,JdbcUtils.getFloat(rs,column.getName()));
                        }else if(dataType.equals(DataType.BigDecimal)){
                            BigDecimal bd = rs.getBigDecimal(column.getName());
                            if(bd!=null){
                                f.set(t,bd.doubleValue());
                            }
                        }else if(dataType.equals(DataType.TIME)){
                            f.set(t,rs.getTime(column.getName()));
                        }else{
                            f.set(t,rs.getObject(column.getName()));
                        }
                    }
                }
            }
            return t;
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException(String.format("将结果集转换为POJO类[%s]时异常,异常信息:%s.",requiredType.getName(),e.getMessage()));
        }
	}
	/**
	 * 将resultset的一记录转换为一个POJO
	 * dbColumns在从数据集中获取数据前判断是否存在这个字段，若不存在，则不获取
	 * @param requiredType
	 * @param rs
	 * @return
	 * @throws Exception
	 */
	public static <T> T fromResultSetToPojo_old(Class<T> requiredType,
			ResultSet rs,Map<String,Column> dbColumns){
		String db_field_name = "";
		T t = null;
        try{
           t = requiredType.newInstance();
        }catch (Exception e){
            throw new RuntimeException(String.format("实例化类[%s]是失败,异常:%s.",requiredType.getName(),e.getMessage()));
        }
		for(Field f : requiredType.getDeclaredFields()){
            j.util.jdbc.simple.annotation.Column c = f.getAnnotation(j.util.jdbc.simple.annotation.Column.class);
			if(null == c){
				db_field_name = f.getName();
			}else{
				if(c.noUsed())continue;
				db_field_name = c.name();
                if(StringUtils.isNullOrEmpty(db_field_name)){
                    db_field_name = f.getName();
                }
			}
			if(dbColumns.containsKey(db_field_name.toUpperCase())){
				try{
                    Object v = rs.getObject(db_field_name);
                    if(null != v){
                        //使其支持获取oracle的clob类型
                        /*if(v instanceof oracle.sql.CLOB){
                            oracle.sql.CLOB clob = (oracle.sql.CLOB)v;
                            BufferedReader reader = new BufferedReader(clob.getCharacterStream());
                            StringBuilder sb = new StringBuilder();
                            String str = "";
                            while((str=reader.readLine())!=null){
                                sb.append(str);//将每一行拼接到clobStr上
                                //增加换行符
                                sb.append(StringUtils.LINE_SEPARATOR);
                            }
                            v = sb;
                        }*/
                        f.setAccessible(true);
                        BeanUtils.setProperty(t, f.getName(), v);
                    }
				}catch(Exception e){
                    throw new RuntimeException(String.format("类[%s]为属性[%s]赋值是异常:%s.",requiredType.getName(),f.getName(),e.getMessage()));
				}
			}
		}
		return t;
	}
    /**
     * 将resultset的一记录转换为一个POJO
     * dbColumns在从数据集中获取数据前判断是否存在这个字段，若不存在，则不获取
     * @param requiredType
     * @param rs
     * @return
     * @throws Exception
     */
    public static <T> T fromResultSetToPojo(Class<T> requiredType,
                                                ResultSet rs,Collection<Column> columns){
        T t;
        try{
            t = requiredType.newInstance();
            j.util.jdbc.simple.annotation.Column c;
            DataType dataType;
            Class<?> fieldType;
            for(Column column : columns){
                if(column.isSuperClassField()){
                    continue;
                }
                Field f = requiredType.getDeclaredField(column.getEntityAttributeName());
                if(null!=f){
                    f.setAccessible(true);
                    dataType = column.getDataType();
                    fieldType = f.getType();
                    if(dataType.equals(DataType.AUTO)){
                        if(fieldType.equals(String.class)){
                            f.set(t, rs.getString(column.getName()));
                        }else if(fieldType.equals(int.class)){
                            f.setInt(t,rs.getInt(column.getName()));
                        }else if(fieldType.equals(Integer.class)){
                            f.set(t,JdbcUtils.getInteger(rs,column.getName()));
                        }else if(fieldType.equals(Date.class)){
                            //日期类型
                            f.set(t,new Date(rs.getTimestamp(column.getName()).getTime()));
                        }else if(fieldType.equals(Boolean.class)){
                            f.set(t,JdbcUtils.getBoolean(rs,column.getName()));
                        }else if(fieldType.equals(boolean.class)){
                            Boolean b = JdbcUtils.getBoolean(rs,column.getName());
                            if(b != null){
                                f.setBoolean(t,b);
                            }else{
                                f.setBoolean(t,false);
                            }
                        }else{
                            f.set(t,rs.getObject(column.getName()));
                        }
                    }else if(dataType.equals(DataType.BOOLEAN)){
                        f.setBoolean(t, rs.getBoolean(column.getName()));
                    }else if(dataType.equals(DataType.DATE)){
                        f.set(t,rs.getDate(column.getName()));
                    }else if(dataType.equals(DataType.DATETIME)){
                        f.set(t, JdbcUtils.getDateFromResultSet(rs, column.getName()));
                    }else if(dataType.equals(DataType.DOUBLE)){
                        f.set(t,JdbcUtils.getDouble(rs, column.getName()));
                    }else if(dataType.equals(DataType.FLOAT)){
                        f.set(t,JdbcUtils.getFloat(rs,column.getName()));
                    }else if(dataType.equals(DataType.BigDecimal)){
                        BigDecimal bd = rs.getBigDecimal(column.getName());
                        if(bd!=null){ 
                            if(fieldType.equals(Double.class)){
                                f.set(t,bd.doubleValue());
                            }else if(fieldType.equals(Long.class)){
                                f.set(t,bd.longValue());
                            }else if(fieldType.equals(Float.class)){
                                f.setFloat(t,bd.floatValue());
                            }else if(fieldType.equals(long.class)){
                                f.setLong(t,bd.longValue());
                            }else if(fieldType.equals(double.class)){
                                f.setDouble(t, bd.doubleValue());
                            }else if(fieldType.equals(float.class)){
                                f.setFloat(t, bd.floatValue());
                            }
                        }else{
                            f.set(t,null);
                        }
                    }else if(dataType.equals(DataType.TIME)){
                        f.set(t, rs.getTime(column.getName()));
                    }else{
                        f.set(t,rs.getObject(column.getName()));
                    }
                }
            }
            return t;
        }catch(Exception e){
            throw new RuntimeException(String.format("将结果集转换为POJO类[%s]时异常,异常信息:%s.",requiredType.getName(),e.getMessage()));
        }
    }
	/**
	 * 将Resultset转换为map，key为字段名，且大写
	 * 传递ResultSet需要next()
	 * @param rs
	 * @return
	 * @throws Exception
	 */
	public static Map<String,Object> fromResultSetToMap(ResultSet rs)throws Exception{
		Map<String,Object> retMap = New.map();
		ResultSetMetaData md = rs.getMetaData();
		int columnCount = md.getColumnCount();
		String[] columnNames = new String[columnCount];
		for(int i=0;i<columnCount;i++){
			columnNames[i] = md.getColumnLabel(i+1);
		}
		for(String s : columnNames){
			retMap.put(s.toUpperCase(), rs.getObject(s));
		}
		return retMap;
	}
	/**
	 * 将以实体属性名字为key的map转换成以该名字所映射的数据库字段名字的map
	 * @param entityAttributeColumnMap 实体的名字与数据库字段名字的映射关系map
	 * @param args
	 * @return
	 */
	public static Map<String,Object> toDbColumnKeyMap(Map<String,String> entityAttributeColumnMap,Map<String,Object> args){
		Map<String,Object> map = New.map(args.size());
		String dbColumnKey;
		for(Map.Entry<String, Object> entry : args.entrySet()){
			dbColumnKey = entityAttributeColumnMap.get(entry.getKey().toUpperCase());
			if(StringUtils.isNotEmpty(dbColumnKey)){
				map.put(dbColumnKey, entry.getValue());
			}else{
				throw new RuntimeException("args's key must is entity's attribute name.");
			}
		}
		return map;
	}
	
	public static Map<String,Object> getPkArgs(Object obj){
		Table table = (Table)obj.getClass().getAnnotation(Table.class);
		if(null==table){
			return null;
		}
		String pkColumns = table.pkColumns();
		if(StringUtils.isNullOrEmpty(pkColumns)){
			return null;
		}
		Map<String,Object> args = New.lMap();
		for(String s : pkColumns.split(",")){
			args.put(s.toUpperCase(), null);
		}
		String tmp,key;
		for(Field f : obj.getClass().getDeclaredFields()){
			tmp = f.getName();
			key = tmp.toUpperCase();
			if(args.containsKey(key)){
				try {
					args.put(key, BeanUtils.getProperty(obj, tmp));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return args;
	}
	
	public static void main(String[] args) {
		String s = "a$b$c";
		String[] arr = s.split("\\$");
		System.out.println(arr[0]);
	}
}
